package cn.edu.gzgs.config.ratelimit;

import cn.edu.gzgs.dto.LoginRequestDTO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.edu.gzgs.vo.ApiResponse; // 确保 ApiResponse 已导入或在同一包结构下

import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class LoginRateLimitAspect {

    private final StringRedisTemplate redisTemplate;
    private static final String RATE_LIMIT_IP_PREFIX = "ratelimit:login:ip:";
    private static final String RATE_LIMIT_USER_PREFIX = "ratelimit:login:user:";

    @Value("${application.login-ratelimit.ip.limit:5}")
    private int ipLimit;

    @Value("${application.login-ratelimit.ip.window-seconds:60}")
    private long ipWindowSeconds;

    @Value("${application.login-ratelimit.user.limit:5}")
    private int userLimit;

    @Value("${application.login-ratelimit.user.window-seconds:60}")
    private long userWindowSeconds;

    @Autowired
    public LoginRateLimitAspect(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(loginRateLimit)")
    public Object around(ProceedingJoinPoint joinPoint, LoginRateLimit loginRateLimit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ipAddress = getClientIpAddress(request);

        // 1. IP维度限流检查
        String ipKey = RATE_LIMIT_IP_PREFIX + ipAddress;
        if (!checkLimit(ipKey, ipLimit, ipWindowSeconds, "IP地址")) {
            return createRateLimitErrorResponse();
        }

        // 2. 用户名/工号维度限流检查 (只有当请求参数是 LoginRequestDTO 时)
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0 && args[0] instanceof LoginRequestDTO) {
            LoginRequestDTO loginRequest = (LoginRequestDTO) args[0];
            String userNo = loginRequest.getUserNo();
            if (userNo != null && !userNo.isEmpty()) {
                String userKey = RATE_LIMIT_USER_PREFIX + userNo;
                if (!checkLimit(userKey, userLimit, userWindowSeconds, "用户账号")) {
                    return createRateLimitErrorResponse();
                }
            }
        }

        // 如果通过限流检查，则执行原方法
        return joinPoint.proceed();
    }

    private boolean checkLimit(String key, int limit, long windowSeconds, String limitType) {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        String currentValueStr = ops.get(key);

        if (currentValueStr == null) {
            // 首次访问，设置计数为1，并设置过期时间
            ops.set(key, "1", windowSeconds, TimeUnit.SECONDS);
            return true;
        } else {
            long currentValue = Long.parseLong(currentValueStr);
            if (currentValue < limit) {
                // 未达到限制，增加计数
                ops.increment(key);
                return true;
            } else {
                // 已达到限制
                log.warn("登录接口限流触发 - 类型: {}, Key: {}, 限制: {}次/{}秒", limitType, key, limit, windowSeconds);
                return false;
            }
        }
    }

    private ResponseEntity<ApiResponse<Object>> createRateLimitErrorResponse() {
        ApiResponse<Object> errorResponse = new ApiResponse<>(HttpStatus.TOO_MANY_REQUESTS.value(), "操作过于频繁，请稍后再试", null);
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(errorResponse);
    }

    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedForHeader = request.getHeader("X-Forwarded-For");
        if (xForwardedForHeader != null && !xForwardedForHeader.isEmpty()) {
            return xForwardedForHeader.split(",")[0].trim();
        }
        return request.getRemoteAddr();
    }
} 